#pragma once
#include<pthread.h>
  #include <semaphore.h>
#include<vector>


int defaultcapacity=20;//设置最大容量为20

template<class T>
class RingQueue
{
    private:
    //封装一下信号量的pv操作
    void P(sem_t*sem)//传引用取地址跟传地址直接用地址有区别吗？
    {
        //P操作是申请自己关注的资源，申请成功--自己关注的资源，信号量的申请相当于对资源的预约
        sem_wait(sem);//申请信号量失败，申请的执行流（线程或进程）会卡在这里阻塞等待，直到有信号量被释放
        
    }
    void V(sem_t* sem)
    {
        //V操作是完事之后++对方的资源：例：获取完数据++对方的空间资源
        sem_post(sem);
    }
    public:
    RingQueue(int capacity=defaultcapacity)
    :max_capacity(capacity),ring_queue(capacity),spt_consummer(0),spt_producter(0)//vector开空间
    {
        //局部的锁跟条件变量需要初始化
        pthread_mutex_init(&mutex_,nullptr);//第二个参数设置null

        //信号量的初始化工作
        sem_init(&data_,0,0);//第三个参数一开始关注的资源的数量是多少
        sem_init(&space_,0,max_capacity);//生产者关注的是剩余空间的多少

    }
    void Pop(T* out)
    {
        P(&data_);
        Lock(&mutex_);
        //1.获取数据
        *out=ring_queue[spt_consummer];
        //2.获取完后下标往后移动并形成环状结构
        ++spt_consummer;
        spt_consummer%=max_capacity;
        UnLock(&mutex_);
        V(&space_);
    //确保第一个拿到锁的人第一个出来
    }
    void Push(const T& task)
    {
        //生产者push数据进来前先申请信号量
        P(&space_);//这一步如果申请失败会怎么样？会阻塞等待//相当于对资源的判断+判断失败到等待队列里等//这一步是原子性的
        Lock(&mutex_);//到这里说明申请到信号量了，有资源存在可以访问资源了，访问前上锁解决多线程并发访问带来的数据不一致的问题
        //1.存放数据->存放在生产者下标->这个下标代表的是生产者下一个存放数据的位置
        ring_queue[spt_producter]=task;
        //2.存放完下标往下一个位置移动，且让它形成环状结构
        ++spt_producter;
        spt_producter%=max_capacity;//例：最大容量5，下标%5= 0、1、2、3、4
        UnLock(&mutex_);
        V(&data_);//完事之后对对方关注的资源++
    }
    ~RingQueue()
    {
        //局部的锁跟条件变量需要销毁
        pthread_mutex_destroy(&mutex_);

        //信号量的销毁工作
        sem_destroy(&data_);
        sem_destroy(&space_);
    
    }
    private:
    int Lock(pthread_mutex_t*mutex)
    {
       return pthread_mutex_lock(mutex);
    }
    int UnLock(pthread_mutex_t* mutex)
    {
        return pthread_mutex_unlock(mutex);
    }
    int max_capacity;//环形队列的最大容量
    std::vector<T> ring_queue;//基于队列的生环形产消费者模型
    pthread_mutex_t mutex_;//为了解决多线程并发引发的数据不一致的问题需要上锁


    int spt_consummer;//消费者下一个消费的下标
    int spt_producter;//生产者下一个存放数据的下标

    //信号量其实就是一把计数器，对信号量的申请等于对资源的预约
    sem_t data_;//消费者关注的资源：数据
    sem_t space_;//生产者关注的资源：空间
};